package org.kosmix.kosmosfs.access;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.WritableByteChannel;

public class KfsOutputChannel implements WritableByteChannel, Positionable {
	private static final int DEFAULT_BUF_SIZE = 1048576;
	private ByteBuffer writeBuffer;
	private int kfsFd = -1;
	private long cPtr;

	private static final native int close(long paramLong, int paramInt);

	private static final native int write(long paramLong, int paramInt1,
			ByteBuffer paramByteBuffer, int paramInt2, int paramInt3);

	private static final native int sync(long paramLong, int paramInt);

	private static final native int seek(long paramLong1, int paramInt,
			long paramLong2);

	private static final native long tell(long paramLong, int paramInt);

	public KfsOutputChannel(long ptr, int fd) {
		this.writeBuffer = ByteBuffer.allocateDirect(1048576);
		this.writeBuffer.clear();

		this.kfsFd = fd;
		this.cPtr = ptr;
	}

	public boolean isOpen() {
		return this.kfsFd > 0;
	}

	public int write(ByteBuffer src) throws IOException {
		if (this.kfsFd < 0) {
			throw new IOException("File closed");
		}
		int r0 = src.remaining();

		while (src.hasRemaining()) {
			if (this.writeBuffer.remaining() == 0) {
				this.writeBuffer.flip();
				writeDirect(this.writeBuffer);
			}

			int lim = src.limit();

			if (this.writeBuffer.remaining() < src.remaining())
				src.limit(src.position() + this.writeBuffer.remaining());
			this.writeBuffer.put(src);

			src.limit(lim);
		}

		int r1 = src.remaining();
		return r0 - r1;
	}

	private void writeDirect(ByteBuffer buf) throws IOException {
		if (!buf.isDirect()) {
			throw new IllegalArgumentException("need direct buffer");
		}
		int pos = buf.position();
		int last = buf.limit();

		if (last - pos == 0) {
			return;
		}
		int sz = write(this.cPtr, this.kfsFd, buf, pos, last);

		if (sz < 0) {
			throw new IOException("writeDirect failed");
		}

		if (sz == last) {
			buf.clear();
			return;
		}

		if (sz == 0) {
			return;
		}

		ByteBuffer temp = ByteBuffer.allocateDirect(1048576);
		temp.put(buf);
		temp.flip();
		buf.clear();
		buf.put(temp);
	}

	public int sync() throws IOException {
		if (this.kfsFd < 0) {
			throw new IOException("File closed");
		}

		this.writeBuffer.flip();
		writeDirect(this.writeBuffer);

		return sync(this.cPtr, this.kfsFd);
	}

	public int seek(long offset) throws IOException {
		if (this.kfsFd < 0) {
			throw new IOException("File closed");
		}
		sync();

		return seek(this.cPtr, this.kfsFd, offset);
	}

	public long tell() throws IOException {
		if (this.kfsFd < 0) {
			throw new IOException("File closed");
		}

		return tell(this.cPtr, this.kfsFd) + this.writeBuffer.remaining();
	}

	public void close() throws IOException {
		if (this.kfsFd < 0) {
			return;
		}
		sync();

		close(this.cPtr, this.kfsFd);
		this.kfsFd = -1;
	}

	protected void finalize() throws Throwable {
		if (this.kfsFd < 0)
			return;
		close();
	}
}